﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Brainee;
using System.Drawing.Drawing2D;

namespace Brainee.NeuralNetwork
{
  public partial class NetworkView : UserControl
  {
    #region Private fields

    private Network _network;

    #endregion
    #region Public Properties

    [Browsable (false)]
    public Network Network 
    {
      get { return _network; }
      set
      {
        _network = value;
        Invalidate ();
      }
    }

    public Color NeuronColor { get; set; }
    public Color LinesColor { get; set; }

    #endregion
    #region Construction

    public NetworkView ()
    {
      InitializeComponent ();

      BackColor = Color.FromArgb (240, 240, 255);
      LinesColor = Color.FromArgb (32, 64, 96);
      NeuronColor = Color.FromArgb (192, 192, 255);
    }

    #endregion
    #region Paint

    private void NetworkView_Paint (object sender, PaintEventArgs e)
    {
      // Set High quality rendering mode
      e.Graphics.SmoothingMode = SmoothingMode.HighQuality;

      // Fill background
      using (Brush b = new SolidBrush (BackColor))
      {
        e.Graphics.FillRectangle (b, e.ClipRectangle);
      }

      // Return if no binded network
      if (Network == null)
        return;

      // Initialize resources
      Pen linePen = new Pen (LinesColor, 2);
      Brush neuronBrush = new SolidBrush (NeuronColor);

      // Initialize radius of neurons
      int radius = 20;

      int colWidth = this.Width / Network.Count;
      int colOffset = colWidth / 2;

      foreach (Layer l in Network)
      {
        int rowHeight = this.Height / l.OutputCount;
        int rowOffset = rowHeight / 2;

        foreach (Neuron n in l)
        {
          //
          // Draw dendrites
          //
          if (l.Previous == null)
          {
            e.Graphics.DrawLines (linePen, new Point [] {
              new Point(colOffset - colWidth / 2, rowOffset),
              new Point(colOffset, rowOffset),
            });
          }
          if (l.Next == null)
          {
            e.Graphics.DrawLines (linePen, new Point [] {
              new Point(colOffset, rowOffset),
              new Point(colOffset + colWidth / 2, rowOffset),
            });
          }
          else
          {
            int nextrows = l.Next.OutputCount;
            int nextrowHeight = this.Height / nextrows;

            int nextrowOffset = nextrowHeight / 2;
            foreach (Neuron pn in l.Next)
            {
              e.Graphics.DrawLines (linePen, new Point [] {
                new Point(colOffset, rowOffset),
                new Point(colOffset + colWidth, nextrowOffset),
              });
              nextrowOffset += nextrowHeight;
            }
          }

          //
          // Draw nucleus
          //
          Rectangle ellipse = new Rectangle(colOffset - radius, rowOffset - radius, 2 * radius, 2 * radius);
          e.Graphics.FillEllipse (neuronBrush, ellipse);
          e.Graphics.DrawEllipse (linePen, ellipse);

          //
          // Draw Activation function
          //
          if (n.Activation is IdentityFunction)
          {
            e.Graphics.DrawLines (linePen, new Point [] {
              new Point(colOffset + radius / 2, rowOffset - radius / 2),
              new Point(colOffset - radius / 2, rowOffset + radius / 2),
            });
          }
          else if (n.Activation is HeavisideFunction)
          {
            e.Graphics.DrawLines (linePen, new Point [] {
              new Point(colOffset + radius / 2, rowOffset - radius / 2),
              new Point(colOffset, rowOffset - radius / 2),
              new Point(colOffset, rowOffset + radius / 2),
              new Point(colOffset - radius / 2, rowOffset + radius / 2),
            });
          }
          else if (n.Activation is SigmoidFunction)
          {
            e.Graphics.DrawBezier (linePen, 
              new Point (colOffset + radius / 2, rowOffset - radius / 2),
              new Point (colOffset - radius / 4, rowOffset - radius / 2),
              new Point (colOffset + radius / 4, rowOffset + radius / 2),
              new Point (colOffset - radius / 2, rowOffset + radius / 2)
            );
          }


          rowOffset += rowHeight;
        }

        colOffset += colWidth;
      }

      // Release resources
      linePen.Dispose ();
      neuronBrush.Dispose();
    }

    #endregion
  }
}
